package com.cicadasmall.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cicadasmall.common.base.BaseService;
import com.cicadasmall.common.base.LoginUser;
import com.cicadasmall.common.constant.Constant;
import com.cicadasmall.common.exception.ServiceException;
import com.cicadasmall.common.func.Fn;
import com.cicadasmall.common.resp.R;
import com.cicadasmall.common.utils.SecurityUtils;
import com.cicadasmall.system.dto.MenuInputDTO;
import com.cicadasmall.system.dto.MenuQueryDTO;
import com.cicadasmall.system.dto.MenuUpdateDTO;
import com.cicadasmall.data.domain.MenuDO;
import com.cicadasmall.system.service.IMenuService;
import com.cicadasmall.system.vo.MenuVO;
import com.cicadasmall.system.vo.RouteVo;
import com.cicadasmall.data.mapper.SysMenuMapper;
import com.cicadasmall.system.wrapper.MenuWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author westboy
 * @date 2019-07-10
 */
@Service
public class MenuServiceImpl extends BaseService<SysMenuMapper, MenuDO> implements IMenuService {


    @Override
    public List<MenuVO> findList(MenuQueryDTO menuQueryDTO) {
        LambdaQueryWrapper<MenuDO> lambdaQueryWrapper = getLambdaQueryWrapper().orderByAsc(MenuDO::getSortId);

        if (Fn.isNotNull(menuQueryDTO.getParentId())) {
            lambdaQueryWrapper.eq(MenuDO::getParentId, menuQueryDTO.getParentId());
        }

        if (Fn.isNotEmpty(menuQueryDTO.getMenuName())) {
            lambdaQueryWrapper.like(MenuDO::getMenuName, menuQueryDTO.getMenuName());
        }

        List<MenuDO> menuList = baseMapper.selectList(lambdaQueryWrapper);

        return MenuWrapper.newBuilder().listVO(menuList);
    }

    @Override
    public List<MenuVO> getTree() {
        List<MenuDO> menuList = baseMapper.selectList(getLambdaQueryWrapper().orderByAsc(MenuDO::getSortId));
        return MenuWrapper.newBuilder().treeVO(menuList);
    }

    private void checkAndUpdate(MenuDO menu) {

        MenuDO currentMenu = getById(menu.getMenuId());

        if (Fn.isNull(currentMenu)) {
            throw new ServiceException("参数错误！");
        }

        if (Fn.equal(menu.getParentId(), currentMenu.getMenuId())) {
            throw new ServiceException("上级菜单不能为为自己！");
        }

        List<MenuDO> currentMenuChildList = findByParentId(currentMenu.getMenuId());

        if (Fn.isNotNull(menu.getParentId()) && Fn.notEqual(Constant.PARENT_ID, menu.getParentId()) && Fn.notEqual(menu.getParentId(), currentMenu.getParentId())) {

            MenuDO currentParentMenu = getById(menu.getParentId());

            if (currentParentMenu == null) {
                throw new ServiceException("上级菜单不存在！");
            }

            if (Fn.isNotEmpty(currentMenuChildList)) {
                AtomicReference<Boolean> error = new AtomicReference<>(false);
                currentMenuChildList.parallelStream().forEach(e -> error.set(e.getMenuId().equals(currentParentMenu.getMenuId())));
                if (error.get()) {
                    throw new ServiceException("上级菜单选择有误！");
                }
            }

            currentParentMenu.setHasChildren(true);
            updateById(currentParentMenu);

        }

        //Jin 判断当前菜单是否拥有下级菜单
        menu.setHasChildren(Fn.isNotEmpty(currentMenuChildList));

        updateById(menu);

        MenuDO quondamParentMenu = getById(currentMenu.getParentId());
        if (Fn.isNotNull(quondamParentMenu)) {
            List<MenuDO> parentMenuChildList = findByParentId(currentMenu.getParentId());
            if (CollectionUtil.isEmpty(parentMenuChildList)) {
                quondamParentMenu.setHasChildren(false);
                updateById(quondamParentMenu);
            }
        }

    }


    private void checkAndSave(MenuDO menuDO) {
        MenuDO parentMenu = null;

        if (Fn.isNotNull(menuDO.getParentId())) {
            if (!Constant.PARENT_ID.equals(menuDO.getParentId())) {
                parentMenu = getById(menuDO.getParentId());
                if (parentMenu == null) {
                    throw new ServiceException("上级菜单选择有误！");
                }
            }
        } else {
            menuDO.setParentId(Constant.PARENT_ID);
        }

        save(menuDO);

        if (Fn.isNotNull(parentMenu)) {
            parentMenu.setHasChildren(true);
            updateById(parentMenu);
        }

    }

    @Override
    public List<MenuDO> findByParentId(Integer parentId) {
        return baseMapper.selectList(getLambdaQueryWrapper().eq(MenuDO::getParentId, parentId));
    }

    @Override
    public List<RouteVo> findRoutes() {
        LoginUser loginUser = SecurityUtils.getCurrentLoginUser();
        if (Fn.isNotNull(loginUser) && Fn.isNotEmpty(loginUser.getRoleIds())) {
            List<MenuDO> menuList = findByRoleIds(loginUser.getRoleIds());
            if (Fn.isNotEmpty(menuList)) {
                List<MenuVO> treeList = MenuWrapper.newBuilder().treeVO(menuList);
                treeList.stream().sorted(Comparator.comparing(MenuVO::getSortId));
                return MenuWrapper.newBuilder().routeList(treeList);
            }
        }
        return new ArrayList<>();
    }

    @Override
    public List<MenuDO> findByRoleIds(Set<Integer> roleIds) {
        return baseMapper.selectByRoleIds(roleIds);
    }

    @Override
    public List<MenuDO> findByRoleId(Integer roleId) {
        return baseMapper.selectByRoleId(roleId);
    }


    @Transactional
    @Override
    public R<Boolean> save(MenuInputDTO menuInputDTO) {
        MenuDO menu = menuInputDTO.convertToEntity();
        checkAndSave(menu);
        return R.ok("菜单保存成功！", true);
    }

    @Transactional
    @Override
    public R<Boolean> update(MenuUpdateDTO menuUpdateDTO) {
        MenuDO menu = menuUpdateDTO.convertToEntity();
        checkAndUpdate(menu);
        return R.ok("菜单更新成功！", true);
    }

    @Override
    public R<MenuVO> findById(Serializable id) {
        MenuDO menu = getById(id);
        if (Fn.isNull(menu)) {
            throw new ServiceException("菜单不存在！");
        }
        MenuVO menuVo = MenuWrapper.newBuilder().entityVO(menu);
        if (Fn.isNotNull(menu.getParentId()) && Fn.notEqual(Constant.PARENT_ID, menu.getParentId())) {
            MenuDO parentMenu = getById(menu.getParentId());
            menuVo.setParentName(parentMenu.getMenuName());
        } else {
            menuVo.setParentName("顶级菜单");
        }
        return R.ok(menuVo);
    }

    @Transactional
    @Override
    public R<Boolean> deleteById(Serializable id) {
        LambdaQueryWrapper<MenuDO> queryWrapper = getLambdaQueryWrapper().eq(MenuDO::getParentId, id);
        Integer count = baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new ServiceException("请先删除子菜单后再操作！");
        }

        MenuDO menu = getById(id);

        if (Fn.isNull(menu)) {
            throw new ServiceException("菜单不存在！");
        }

        removeById(id);

        if (Fn.isNotNull(menu.getParentId()) && Fn.notEqual(Constant.PARENT_ID, menu.getParentId())) {
            Integer childCount = baseMapper.selectCount(getLambdaQueryWrapper().eq(MenuDO::getParentId, menu.getParentId()));

            //更新上级菜单状态
            if (childCount <= 0) {
                MenuDO parentMenu = getById(menu.getParentId());
                parentMenu.setHasChildren(false);
                updateById(parentMenu);
            }
        }
        return R.ok(true);
    }

}
